package clientN

import (
	"encoding/json"
	"log"
	"net/url"
	"time"
	"wxp2p/common"

	"github.com/gorilla/websocket"
)

type ClientClient struct {
	ServerAddr             string
	ClientId               string
	ClientType             common.EClientType
	SendChan               chan common.P2PSignal
	RecvChan               chan common.P2PSignal
	WebrtcArray            map[int32]*WebrtcOffer
	BLogin                 bool
	doneChan               chan struct{}
	callbackChan           chan string
	onStatusChangeCallback func(string)
	runFlag                bool
}

func NewClientClient(addr string, clientId string) *ClientClient {
	sc := ClientClient{}
	sc.BLogin = false
	sc.ClientId = clientId
	sc.ServerAddr = addr
	sc.WebrtcArray = make(map[int32]*WebrtcOffer)
	sc.ClientType = common.EClientClient
	sc.runFlag = false
	return &sc
}

func (sc *ClientClient) Run() {
	if !sc.runFlag {
		go sc.callbackLoop()
		go sc.recvChanLoop()
		go sc.dailLoop()
		sc.runFlag = true
	}
}

// 关闭
func (sc *ClientClient) StopRun() {
	if sc.runFlag {
		close(sc.doneChan)
		close(sc.RecvChan)
		// close(sc.callbackChan)
		sc.runFlag = false
	}
}

// 创建新的数据通道，这个操作会在server和client之间通过webrtc创建一个数据通道
func (sc *ClientClient) CreateDataChannel(remoteHost string, remotePort string, localHost string, localPort string) {
	hp := common.HostPortPeer{RemoteHost: remoteHost, RemotePort: remotePort, LocalHost: localHost, LocalPort: localPort}
	content, _ := json.Marshal(&hp)
	sig := common.P2PSignal{SigType: common.ECreateAnswer, Content: string(content)}
	if sc.runFlag {
		common.LogDebug("创建端口映射")
		sc.SendChan <- sig
	}
}

// 给界面反馈状态
func (sc *ClientClient) OnStatusChange(f func(log string)) {
	sc.onStatusChangeCallback = f
}

// 重新连接并注册
func (sc *ClientClient) reDial() {
	common.LogDebug("5秒后重新连接信令服务器")
	time.Sleep(5 * time.Second)
	go sc.dailLoop()
}

func (sc *ClientClient) callbackLoop() {
	if sc.callbackChan != nil {
		close(sc.callbackChan)
	}
	sc.callbackChan = make(chan string)
	for lstr := range sc.callbackChan {
		if sc.onStatusChangeCallback != nil {
			sc.onStatusChangeCallback(lstr)
		}
	}
	common.LogDebug("退出 callback loop")
}

// 处理服务器返回的消息
func (sc *ClientClient) recvChanLoop() {
	sc.RecvChan = make(chan common.P2PSignal)
	for sig := range sc.RecvChan {
		if sig.SigType == common.EClientIdDublite {
			// ID重复
			log.Println(sig.Content)
			sc.callbackChan <- sig.Content
			continue
		} else if sig.SigType == common.ENoServerClient {
			sc.StopRun()
			continue
		} else if sig.SigType == common.ECreateAnswerOK {
			// 创建webrtc answer
			hp := common.HostPortPeer{}
			json.Unmarshal([]byte(sig.Content), &hp)
			wa := NewWebrtcOffer(sig.WebrtcId, &hp)
			wa.SignalClient = sc
			sc.WebrtcArray[wa.WebrtcId] = wa
			wa.Run()
			continue
		} else if sig.SigType == common.ELinkOK {
			sc.BLogin = true
			sc.callbackChan <- "ok"
			continue
		}
		wr, ok := sc.WebrtcArray[sig.WebrtcId]
		if ok {
			wr.SignalHandler(sig)
		}
	}
	common.LogDebug("退出 recv loop")
}

// 开始连接，断线自动重连
func (sc *ClientClient) dailLoop() {
	sc.SendChan = make(chan common.P2PSignal)
	u := url.URL{Scheme: "ws", Host: sc.ServerAddr, Path: "/v2/signal"}
	conn, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
	defer func() {
		conn.Close()
		close(sc.SendChan)
		common.LogDebug("退出dial loop")
		sc.runFlag = false
		sc.callbackChan <- "quit"
	}()
	if err != nil {
		sc.reDial()
		return
	} else {
		log.Println("成功连接到信号服务器")

	}
	go sc.linkClient()
	sc.runFlag = true
	sc.doneChan = make(chan struct{})

	readdone := make(chan struct{})
	// 接收信号,连接关闭后线程退出
	go func() {
		defer close(readdone)
		for {
			sig := common.P2PSignal{}
			err := conn.ReadJSON(&sig)
			if err != nil {
				break
			}
			common.LogDebug("接收到信令: ", sig)
			sc.RecvChan <- sig
		}
		common.LogDebug("退出for conn.read json")
	}()
	// 发送信号
	senddone := make(chan struct{})
	go func() {
		defer close(senddone)
		for sig := range sc.SendChan {
			sig.ClientType = sc.ClientType
			sig.ClientId = sc.ClientId
			err := conn.WriteJSON(sig)
			if err != nil {
				break
			}
			common.LogDebug("发送信令:", sig)
		}
		common.LogDebug("退出for send chan")
	}()
	// 阻塞线程退出
	for {
		select {
		case <-readdone: //接收/发送消息出现错误，直接退出线程，或者【重新连接】
			sc.reDial()
			return
		case <-senddone: //接收/发送消息出现错误，直接退出线程，或者【重新连接】
			sc.reDial()
			return
		case <-sc.doneChan:
			return
		}
	}
}

// 发送连接信令
func (sc *ClientClient) linkClient() {
	p2psig := common.P2PSignal{SigType: common.ELinkClient, Content: ""}
	sc.SendChan <- p2psig
}
